<?php

/*
 * Copyright (C) 2009 - 2011 Pham Cong Dinh
 *
 * This file is part of Spica.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
// namespace spica\core\validator;

/**
 * Interface representing any object that can be validated.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
interface SpicaValidatable
{
    /**
     * Retrieves the value to be validated.
     *
     * @return mixed the value to be validated
     */
    public function getValue();

    /**
     * Queries the current state of this SpicaValidatable instance.
     *
     * @param  mixed $value Value to validate
     * @return bool true if the object is in a valid state, false if otherwise
     */
    public function isValid($value = null);

    /**
     * Gets the violation message to use if the validation fails.
     *
     * @return string representing the violation message to use if the validation fails
     */
    public function getViolationMessage();

    /**
     * Validates the given value.
     *
     * @throws SpicaValidatorException if the value is not valid.
     * @param  mixed the value to check.
     */
    public function validate($value = null);

    /**
     * Determines whether this value is required.
     *
     * @return bool
     */
    public function isRequired();
}

/**
 * A generic processor that checks an input against a condition.
 *
 * This class expresses and executes validation rules for your objects.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
abstract class SpicaValidator implements SpicaValidatable
{
    /**
     * Violation message to use if the validation fails.
     *
     * @var string
     */
    protected $_violationMessage;

    /**
     * Value to validate
     *
     * @var mixed
     */
    protected $_value;

    /**
     * Determines whether this value is required. Defaults to true.
     *
     * @var bool
     */
    protected $_required = true;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($violationMessage, $required = true)
    {
        $this->_required         = (bool) $required;
        $this->_violationMessage = $violationMessage;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidatable#getValue()
     */
    public function getValue()
    {
        return $this->_value;
    }

    /**
     * Determines whether the provided value is valid according to this validator.
     *
     * @param  mixed $value
     * @return bool true if the value is valid, false otherwise.
     */
    public function isValid($value = null)
    {
        // this method is designed to be overrided in derived classes
        return false;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidatable#getViolationMessage()
     */
    public function getViolationMessage()
    {
        return $this->_violationMessage;
    }

    /**
     * Validates the value based on the constraint represented by this validator.
     *
     * @throws SpicaValidatorException if the value is not valid.
     * @param  mixed $value the value to check.
     * @return null if the value is valid
     */
    public function validate($value = null)
    {
        throw new BadMethodCallException('This method must be overridden in derived classes. ');
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidatable#isRequired()
     */
    public function isRequired()
    {
        return $this->_required;
    }
}

/**
 * A validator that defines the interface for classes that will play the role
 * of group-validators.
 *
 * The simplest criteria of inter-validation in a group of
 * fields is - fields of one group are not required (to be filled or selected)
 * unless one of them is.
 *
 * Another common criteria is used when submitting a password - fields of one group
 * must have the same value.
 *
 * One form's field name can not be registered in more than one SpicaGroupValidator.
 *
 * Five important notes :
 *  1 - With text boxes, password field and radio button, their value
 *      will come as a simple string.
 *  2 - With multiple choice box, their value(s) will come as an array of strings.
 *  3 - When the client does not enter a value or does not make a selection
 *  to a field, the value of that field will come as an empty string, or as
 *  an array of only one string, whose value is empty.
 *  4 - Only name-errormessage pairs of the fields with error messages must
 *  be included in the returned array, and if there aren't any, null
 *  must be returned.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 30, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
abstract class SpicaGroupValidator extends SpicaValidator
{
    /**
     * Creates a validator with the provided violation message.
     *
     * @param array $options An array representing a list of field names
     *                       and their associated violation message where:
     *                         key   - a string representing the name of the
     *                                 field that resulted with an error message
     *                                 (according to validation).
     *                         value - a string representing the error message of the field.
     *                                 or it must return null, in case the
     *                                 validation found no errors.
     * @param bool  $required Determines whether this value is required. Defaults to true
     */
    public function __construct(array $options, $required = true)
    {
        $this->_violationMessage = $violationMessage;
    }

    /**
     * Gets violation messages for set of fields. Violation message that is returned
     * is an array representing a list of field names and their associated
     * violation message where:
     *  key   - a string representing the name of the field that resulted with
     *          an error message (according to validation).
     *  value - a string representing the error message of the field.
     *          or it must return null, in case the validation found no errors.
     *
     * @return array
     */
    public abstract function getViolationMessages();

    /**
     * This is a utility method that can be used to set or customize an error message
     * that may be used by all fields of this group.
     *
     * For example, in a group of password fields you might want to set an error
     * message like "(passwords must be the same)" that will apply to both fields.
     * Implementation classes may supply a default error message, and then let
     * this method to customize it.
     *
     * @param string $groupErrorMessage an error message for all fields of the group.
     */
    public abstract function setGroupErrorMessage($groupErrorMessage);
}

/**
 * A validator that ensures that a value must be not null.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaNotNullValidator extends SpicaValidator
{
    /**
     * Creates a validator with the provided violation message.
     * SpicaNotNullValidator doesn't support $required as the second argument.
     *
     * @param string $violationMessage violation message to use if the validation fails
     */
    public function __construct($violationMessage)
    {
        $this->_violationMessage = $violationMessage;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;
        return (null !== $value);
    }
}

/**
 * A validator that ensures that a value must be provided. Value can be a string,
 * a number, an array of values or an object.
 *
 * A value is considred to be not empty if:
 *
 * + Not empty string. The following characters are considred whitespaces
 *    + control characters: \x00..\x1F
 *    + true whitespace: " "
 *    + \t\n\r\0\f
 * + Not null
 * + true or false boolean value
 * + An array that contains at least an element
 * + An object that contains at least a property
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 02, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaNotEmptyValidator extends SpicaValidator
{
    /**
     * White spaces will be trimmed before comparison or not.
     * FALSE means white spaces will be trimmed.
     *
     * @var bool
     */
    protected $_allowWhitespace = false;

    /**
     * Creates a validator with the provided violation message.
     * SpicaNotEmptyValidator doesn't support $required as the second argument.
     *
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $allowWhitespace Whitespace can be considered valid character or not.
     *               Defaults to false, which means white spaces will be trimmed
     */
    public function __construct($violationMessage = 'This field is required. Please enter a value.', $allowWhitespace = false)
    {
        $this->_violationMessage = $violationMessage;
        $this->_allowWhitespace  = (bool) $allowWhitespace;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;
        return !spica_valid_empty($value, $this->_allowWhitespace);
    }
}

/**
 * A validator that ensures that a value (a string or a number) must contains
 * letters and/or digits only.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 28, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaAlphaNumericValidator extends SpicaValidator
{
    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return (false === $this->_required);
        }

        // A string or a number is accepted
        return spica_valid_alnum($value);
    }
}

/**
 * A validator that ensures that a value (a string or a number) must contains
 * letters and/or digits and/or dashes (-) only.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      May 14, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaAlphaNumericDashOnlyValidator extends SpicaValidator
{
    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return (false === $this->_required);
        }

        $value = str_replace('-', '', $value);
        return (is_int($value) || (is_string($value) && ctype_alnum($value)));
    }
}

/**
 * A validator that ensures that a value is valid alphabetic string (ASCII).
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 28, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaAlphaValidator extends SpicaValidator
{
    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return (false === $this->_required);
        }

        // String is expected
        if (false === is_string($value))
        {
            return false;
        }

        return spica_valid_alpha($value);
    }
}

/**
 * A validator that ensures that a value is valid integer or integer string.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      May 15, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaIntegerValidator extends SpicaValidator
{
    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return (false === $this->_required);
        }

        return (is_string($value) && ctype_digit($value)) || is_int($value);
    }
}

/**
 * A validator that checks if a given value is false or not.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaAssertFalseValidator extends SpicaValidator
{
    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (null === $value && false === $this->_required)
        {
            return true;
        }

        return (false === $value);
    }
}

/**
 * A validator that checks if a given value is true or not.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaAssertTrueValidator extends SpicaValidator
{
    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (null === $value && false === $this->_required)
        {
            return true;
        }

        return (true === $value);
    }
}

/**
 * A validator that ensures that a given value must be a number within accepted range.
 *
 * Apply on numeric values or string representation of the numeric value.
 * Format: [integral-digits,]integral-digits[.fractional-digits][e[sign]exponential-digits]
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaDigitsValidator extends SpicaValidator
{
    /**
     * Maximum number of integral digits accepted for this number.
     * Note: A series of digits ranging from 0 to 9 that specify the integral
     * part of the number.
     *
     * @var int
     */
    protected $_integral;

    /**
     * Maximum number of fractional digits accepted for this number.
     * Note: A series of digits ranging from 0 to 9 that specify the fractional
     * part of the number
     *
     * @var int
     */
    protected $_fractional;

    /**
     * A culture-specific thousands separator symbol.
     *
     * @var string
     */
    protected $_thousandSep;

    /**
     * A culture-specific decimal point symbol.
     *
     * @var string
     */
    protected $_decimalPoint;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int    $integral     Maximum number of integral digits accepted for this number.
     * @param int    $fractional   Maximum number of fractional digits accepted for this number
     * @param string $violationMessage violation message to use if the validation fails
     * @param string $thousandSep  Thousands separator. Defaults to US-style: ","
     * @param string $decimalPoint Decimal point. Defaults to US-style: "."
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($integral, $fractional, $violationMessage, $thousandSep = ',', $decimalPoint = '.', $required = true)
    {
        $this->_integral         = $integral;
        $this->_fractional       = $fractional;
        $this->_thousandSep      = $thousandSep;
        $this->_decimalPoint     = $decimalPoint;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * Sets the maximum digits allowed in integral part of the number.
     *
     * @param int $integral
     */
    public function setIntegral($integral)
    {
        $this->_integral = $integral;
    }

    /**
     * Sets the maximum digits allowed in fractional part of the number.
     *
     * @param int $fractional
     */
    public function setFractional($fractional)
    {
        $this->_fractional = $fractional;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        // Decimal point is acceptable (but not thousands separator)
        if (false === is_numeric($value) || false === is_string($value))
        {
            return false;
        }

        $parts = explode($this->_decimalPoint, $value, 2);

        // No decimal part
        if (count($parts) < 2)
        {
            return 0 === $this->_fractional;
        }

        // Invalid number format
        if (false === is_numeric($parts[1]))
        {
            return false;
        }

        // The number of digits in fractional part is greater the maximum.
        if (strlen($parts[1]) > $this->_fractional)
        {
            return false;
        }

        // The number of digits in integral part is greater the maximum.
        $integralPart = str_replace($this->_thousandSep, '', $parts[0]);

        // Invalid number format
        if (false === is_numeric($integralPart))
        {
            return false;
        }

        // Remove sign symbol
        if (strlen(abs($integralPart)) > $this->_integral)
        {
            return false;
        }

        return true;
    }
}

/**
 * The SpicaRangeValidator object is used to check if a given value falls
 * between two values.
 *
 * It is possible to check ranges within numbers, dates, and characters.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
abstract class SpicaRangeValidator extends SpicaValidator
{
    /**
     * Gets the minimum accepted value for the given value.
     *
     * @return int representing the minimum accepted value for the given value.
     */
    public abstract function getMinValue();

    /**
     * Gets the maximum accepted value for the given value.
     *
     * @return int representing the maximum accepted value for the given value.
     */
    public abstract function getMaxValue();
}

/**
 * A validator that ensures that a given value (numeric string or a number)
 * has to fall between two values. It is used to check ranges within numbers.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaNumericRangeValidator extends SpicaRangeValidator
{
    /**
     * Maximum value for the number.
     *
     * @var int
     */
    protected $_max;

    /**
     * Minimum value for the number.
     *
     * @var int
     */
    protected $_min;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int $min minimum number of integral digits accepted for this number
     * @param int $minRangeBoundaryType This parameter determines how the value
     *            will be compared and can have 2 values.
     *               SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *               SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param int $max maximum number of integral digits accepted for this number
     * @param int $maxRangeBoundaryType This parameter determines how the value
     *            will be compared and can have 2 values.
     *               SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *               SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($min, $max, $violationMessage, $required = true)
    {
        $this->_min = $min;
        $this->_max = $max;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $minRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $minRangeBoundaryType)
        {
            throw new InvalidArgumentException('The $minRangeBoundaryType argument must be 0 or 1.');
        }

        if (SpicaRangeBoundaryType::INCLUSIVE !== $maxRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $maxRangeBoundaryType)
        {
            throw new InvalidArgumentException('The $maxRangeBoundaryType argument must be 0 or 1.');
        }

        $this->_maxRangeBoundaryType = $maxRangeBoundaryType;
        $this->_minRangeBoundaryType = $minRangeBoundaryType;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        if (null !== $value && false === is_scalar($value))
        {
            return false;
        }

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return (false === $this->_required);
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::INCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($value < $this->_min || $this->_max < $value);
        }

        if (SpicaRangeBoundaryType::EXCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::INCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($value <= $this->_min || $this->_max < $value);
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($value < $this->_min || $this->_max <= $value);
        }

        return ($this->_min >= $value || $this->_max <= $value);
    }

    /**
     * Gets the minimum value for the number.
     *
     * @return int representing the minimum value for the number.
     */
    public function getMinValue()
    {
        return $this->_min;
    }

    /**
     * Gets the maximum value for the number.
     *
     * @return int representing the maximum value for the number.
     */
    public function getMaxValue()
    {
        return $this->_max;
    }
}

/**
 * A validator that ensures that a given value is not greater than a certain number.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 25, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMaxValueValidator extends SpicaValidator
{
    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType = 1;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int|float $max maximum value for the number
     * @param int       $rangeBoundaryType This parameter determines how the value
     *                  will be compared and can have 2 values.
     *                  SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                  SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string    $violationMessage violation message to use if the validation fails
     * @param bool      $required Determines whether this value is required. Defaults to true
     */
    public function __construct($max, $rangeBoundaryType = 1, $violationMessage, $required = true)
    {
        $this->_max      = $max;
        $this->_required = (bool) $required;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        if (null !== $value && false === is_scalar($value))
        {
            return false;
        }

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_max >= $value);
        }

        return ($this->_max > $value);
    }

    /**
     * Gets the maximum value for the number.
     *
     * @return int|float representing the maximum value for the number.
     */
    public function getMaxValue()
    {
        return $this->_max;
    }
}

/**
 * A validator that ensures that a given value is not less than a certain number.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 25, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMinValueValidator extends SpicaValidator
{
    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType = 1;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int|float $min minimum value for the number
     * @param int       $rangeBoundaryType This parameter determines how the value
     *                  will be compared and can have 2 values.
     *                  SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                  SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($min, $rangeBoundaryType = 1, $violationMessage, $required = true)
    {
        $this->_min      = $min;
        $this->_required = (bool) $required;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        if (null !== $value && false === is_scalar($value))
        {
            return false;
        }

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_min <= $value);
        }

        return ($this->_min < $value);
    }

    /**
     * Gets the minimum value for the number.
     *
     * @return int|float representing the minimum value for the number.
     */
    public function getMinValue()
    {
        return $this->_min;
    }
}

/**
 * A validator that ensures that a given set of values do not contain duplicate values
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 25, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaUniqueValueValidator extends SpicaValidator
{

}

/**
 * A validator that ensures that a given comma-separated or pipe-separated string
 * contains valid values.
 *
 * Examples: Monday|Tuesday|Wednesday
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 25, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaDelimitedValueValidator extends SpicaValidator
{
    /**
     * String contains delimited symbols such as | or , to separate values.
     *
     * @var string
     */
    protected $_delimitedValue;

    /**
     * Creates an object of <code>SpicaDelimitedValueValidator</code>.
     *
     * @param string $delimitedValue String contains delimited symbols such as | or , to separate values.
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($delimitedValue, $violationMessage, $required = true)
    {
        $this->_delimitedValue   = $delimitedValue;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        return in_array($value, preg_split("/[|,]/", $this->_delimitedValue));
    }
}

/**
 * A validator that ensures that a list of values to inspect is members of a list of
 * predefined values. Strict type comparison is not implemented.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 28, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaAllowedValuesValidator extends SpicaValidator
{
    /**
     * The allowed list of items.
     *
     * @var array
     */
    protected $_allowedValues;

    /**
     * Creates an object of <code>SpicaAllowedValuesValidator</code>.
     *
     * @param array  $allowedValues The allowed list of items.
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($allowedValues, $violationMessage, $required = true)
    {
        if (false === is_array($allowedValues))
        {
            throw new InvalidArgumentException('The argument $parentList must be an array.');
        }

        $this->_allowedValues    = $allowedValues;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * Validates the passed in values.
     *
     * @param  mixed $value List of values to validate
     * @return bool true if the object is in a valid state, false if otherwise
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if ((true === spica_valid_empty_string($value) || 0 === count($value)))
        {
            return false === $this->_required;
        }

        if (false  === is_array($value))
        {
            $value = (array) $value;
        }

        // Returns an array containing all the entries from $value that are not
        // present in any of the given parent list of items.
        return !count(array_diff($value, $this->_allowedValues));
    }

    /**
     * Gets allowed list if items.
     *
     * @return array
     */
    public function getAllowedValues()
    {
        return $this->_allowedValues;
    }
}

/**
 * A validator that checks the number of items selected in a list of values must be
 * equal to a given number.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 28, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaExactElementCountValidator extends SpicaValidator
{
    /**
     * The allowed element count.
     *
     * @var int
     */
    protected $_count;

    /**
     * Creates an object of <code>SpicaExactElementCountValidator</code>.
     *
     * @param int    $count The allowed element count.
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($count, $violationMessage, $required = true)
    {
        $this->_count            = $count;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if ((true === spica_valid_empty_string($value) || 0 === count($value)))
        {
            return false === $this->_required;
        }

        if (false  === is_array($value))
        {
            $value = (array) $value;
        }

        return ($this->_count === count($value));
    }
}

/**
 * A validator that checks the number of items selected in a list of values must be
 * less than or equal to a given number.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 28, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMaxElementCountValidator extends SpicaValidator
{
    /**
     * The maximum element count.
     *
     * @var int
     */
    protected $_maxCount;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType;

    /**
     * Creates an object of <code>SpicaMaxElementCountValidator</code>.
     *
     * @param int    $maxCount The allowed element count.
     * @param int    $rangeBoundaryType This parameter determines how the value
     *               will be compared and can have 2 values.
     *                 SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                 SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($maxCount, $rangeBoundaryType = 1, $violationMessage, $required = true)
    {
        $this->_maxCount         = $maxCount;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
        $this->_required          = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if ((true === spica_valid_empty_string($value) || 0 === count($value)))
        {
            return false === $this->_required;
        }

        if (false  === is_array($value))
        {
            $value = (array) $value;
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_maxCount >= count($value));
        }

        return ($this->_maxCount > count($value));
    }

    /**
     * Gets maximum element count.
     *
     * @return int
     */
    public function getMaxValue()
    {
        return $this->_maxCount;
    }
}

/**
 * A validator that checks the number of items selected in a list of values must be
 * greater than or equal to a given number.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 28, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMinElementCountValidator extends SpicaValidator
{
    /**
     * The minimum element count.
     *
     * @var int
     */
    protected $_minCount;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType;

    /**
     * Creates an object of <code>SpicaMinElementCountValidator</code>.
     *
     * @param int    $minCount The allowed element count.
     * @param int    $rangeBoundaryType This parameter determines how the value
     *               will be compared and can have 2 values.
     *                 SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                 SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($minCount, $rangeBoundaryType = 1, $violationMessage, $required = true)
    {
        $this->_minCount         = $minCount;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
        $this->_required          = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if ((true === spica_valid_empty_string($value) || 0 === count($value)))
        {
            return false === $this->_required;
        }

        if (false  === is_array($value))
        {
            $value = (array) $value;
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_minCount <= count($value));
        }

        return ($this->_minCount < count($value));
    }

    /**
     * Gets maximum element count.
     *
     * @return int
     */
    public function getMinValue()
    {
        return $this->_minCount;
    }
}

/**
 * This validator determines if there is any matching value if a set of values.
 * The validation reports a false when a duplicate is found.
 *
 * The set of values in question must be an array or false will be returned.
 *
 * This validator can be used to check if three or more text boxes, dropdown lists,
 * or list boxes have a matching value.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 01, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaDuplicateEntryValidator extends SpicaValidator
{
    /**
     * Creates an object of <code>SpicaDuplicateEntryValidator</code>.
     *
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($violationMessage, $required = true)
    {
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (null === $value)
        {
            return (false === $this->_required);
        }

        if (false === is_array($value))
        {
            return false;
        }

        return count($value) === count(array_flip(array_values($value)));
    }
}

/**
 * A validator that ensures that the length of a given string is not greater than
 * a certain value.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMaxLengthStringValidator extends SpicaValidator
{
    /**
     * Maximum length of the string.
     *
     * @var int
     */
    protected $_maxLength;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType;

    /**
     * White spaces will be trimmed before comparison or not.
     * FALSE means white spaces will be trimmed.
     *
     * @var bool
     */
    protected $_allowWhitespace = false;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int $maxLength maximum length for the string
     * @param string $violationMessage violation message to use if the validation fails
     * @param int $rangeBoundaryType This parameter determines how the value
     *            will be compared and can have 2 values.
     *            SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *            SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param bool   $allowWhitespace Whitespace can be considered valid character or not.
     *               Defaults to false, which means white spaces will be trimmed
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($maxLength, $violationMessage, $rangeBoundaryType = 1, $allowWhitespace = false, $required = true)
    {
        $this->_maxLength        = $maxLength;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
        $this->_required          = (bool) $required;
        $this->_allowWhitespace   = (bool) $allowWhitespace;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // A string is expected. A integer/float/array/object/number will fail
        if (null !== $value && false === is_string($value))
        {
            return false;
        }

        // No data is provided
        if (true === ($empty = spica_valid_empty_string($value, $this->_allowWhitespace)))
        {
            return false === $this->_required;
        }

        $length = (true === $empty) ? 0 : strlen($value);

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_maxLength >= $length);
        }

        return ($this->_maxLength > $length);
    }

    /**
     * Gets the maximum length for the string.
     *
     * @return int representing the maximum length value for the string.
     */
    public function getMaxValue()
    {
        return $this->_maxLength;
    }
}

/**
 * A validator that ensures that the length of a given string is not less than
 * a certain value.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMinLengthStringValidator extends SpicaValidator
{
    /**
     * Minimum length of the string.
     *
     * @var int
     */
    protected $_minLength;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType;

    /**
     * White spaces will be trimmed before comparison or not.
     * FALSE means white spaces will be trimmed.
     *
     * @var bool
     */
    protected $_allowWhitespace = false;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int $minLength minimum length for the string
     * @param string $violationMessage violation message to use if the validation fails
     * @param int $rangeBoundaryType This parameter determines how the value
     *            will be compared and can have 2 values.
     *            SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *            SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param bool   $allowWhitespace Whitespace can be considered valid character or not.
     *               Defaults to false, which means white spaces will be trimmed
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($minLength, $violationMessage, $rangeBoundaryType = 1, $allowWhitespace = false, $required = true)
    {
        $this->_minLength        = $minLength;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
        $this->_required          = (bool) $required;
        $this->_allowWhitespace   = (bool) $allowWhitespace;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // A string is expected. A integer/float/array/object/number will fail
        if (null !== $value && false === is_string($value))
        {
            return false;
        }

        // No data is provided but this value is optional (not mandatory)
        if (true === ($empty = spica_valid_empty_string($value, $this->_allowWhitespace)))
        {
            return false === $this->_required;
        }

        $length = (true === $empty) ? 0 : strlen($value);

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_minLength <= $length);
        }

        return ($this->_minLength < $length);
    }

    /**
     * Gets the minimum length for the string.
     *
     * @return int representing the minimum length value for the string.
     */
    public function getMinValue()
    {
        return $this->_maxLength;
    }
}

/**
 * A validator that validates against a string's length.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaStringLengthValidator extends SpicaRangeValidator
{
    /**
     * Maximum length of the string.
     *
     * @var int
     */
    protected $_maxLength;

    /**
     * Minimum length of the string.
     *
     * @var int
     */
    protected $_minLength;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_minRangeBoundaryType;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_maxRangeBoundaryType;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int|float $minLength minimum length for the string
     * @param int       $minRangeBoundaryType This parameter determines how the value
     *                  will be compared and can have 2 values.
     *                  SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                  SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param int|float $maxLength maximum length for the string
     * @param int       $maxRangeBoundaryType This parameter determines how the value
     *                  will be compared and can have 2 values.
     *                  SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                  SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($minLength, $minRangeBoundaryType = 1, $maxLength, $maxRangeBoundaryType = 1, $violationMessage, $required = true)
    {
        $this->_minLength        = $minLength;
        $this->_maxLength        = $maxLength;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $minRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $minRangeBoundaryType)
        {
            throw new InvalidArgumentException('The $minRangeBoundaryType argument must be 0 or 1.');
        }

        if (SpicaRangeBoundaryType::INCLUSIVE !== $maxRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $maxRangeBoundaryType)
        {
            throw new InvalidArgumentException('The $maxRangeBoundaryType argument must be 0 or 1.');
        }

        $this->_maxRangeBoundaryType = $maxRangeBoundaryType;
        $this->_minRangeBoundaryType = $minRangeBoundaryType;
        $this->_required             = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (false === is_string($value))
        {
            return false;
        }

        $length = strlen($value);

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::INCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($length < $this->_minLength || $this->_maxLength < $length);
        }

        if (SpicaRangeBoundaryType::EXCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::INCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($length <= $this->_minLength || $this->_maxLength < $length);
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($length < $this->_minLength || $this->_maxLength <= $length);
        }

        return ($this->_minLength >= $length || $this->_maxLength <= $length);
    }

    /**
     * Gets the minimum length for the string.
     *
     * @return int representing the minimum length value for the string.
     */
    public function getMinValue()
    {
        return $this->_minLength;
    }

    /**
     * Gets the maximum length for the string.
     *
     * @return int representing the maximum length value for the string.
     */
    public function getMaxValue()
    {
        return $this->_maxLength;
    }
}

/**
 * A validator that compares a list of words to the text (whose words are separated
 * in spaces - Japanese is not supported). It reports a false when any of the
 * words is found within the text.
 *
 * Use it to block bad language and other words that are inappropriate.
 *
 * @todo Defines rules to catch words where letters are separated by other characters like,
 * if you are looking for "jerk", you will find it in "j*e!r$k".
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 02, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaUnwantedWordsValidator extends SpicaValidator
{
    /**
     * Unwanted word list.
     *
     * @var array
     */
    protected $_words = array();

    /**
     * String is compared in case sensitive mode or not.
     *
     * @var bool
     */
    protected $_caseSensitive;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param array  $words Unwanted word list.
     * @param array  $caseSensitive String is compared in case sensitive mode or not.
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($words, $caseSensitive = false, $violationMessage, $required = true)
    {
        $this->_words            = (array) $words;
        $this->_caseSensitive    = (bool) $caseSensitive;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid($value)
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (false === is_string($value))
        {
            return false;
        }

        $words   = $this->_words;
        if (true === $this->_caseSensitive)
        {
            $value = strtolower($value);
            $words = array_map('strtolower', $words);
        }

        $wordsToValidate = explode(' ', $value);

        foreach ($words as $word)
        {
            if (true === in_array($word, $wordsToValidate))
            {
                return false;
            }
        }

        return true;
    }
}

/**
 * A validator that ensures that the number of words in a string is not
 * greater than a maximum.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 30, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMaxWordCountValidator extends SpicaValidator
{
    /**
     * Maximum number of words of the string.
     *
     * @var int
     */
    protected $_maxWord;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType;

    /**
     * A list of additional characters that are considered valid characters to compose a word.
     *
     * @var string
     */
    protected $_specialCharacters = 'àáạảãâậẫấầẩăặằắẳặẵçòóọỏõơởớợờôồốộổỗùúụủũừứưửữựỉịíìÀÁẠẢÃÂẬẪẤẦẨĂẶẰẮẲẶẴÇÒÓỌỎÕƠỞỚỢỜÔỒỐỘỔỖÙÚỤỦŨỪỨƯỬỮỰỈỊÍÌüÜñÑäÄöÖüß€$¿₧';

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int $maxWord maximum number of words in a string
     * @param int $rangeBoundaryType This parameter determines how the value
     *            will be compared and can have 2 values.
     *            SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *            SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     * @param string $specialCharacters A list of additional characters that are considered valid characters to compose a word
     */
    public function __construct($maxWord, $rangeBoundaryType = 1, $violationMessage, $required = true, $specialCharacters = null)
    {
        $this->_maxWord          = $maxWord;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType  = $rangeBoundaryType;
        $this->_required           = (bool) $required;

        if (null !== $specialCharacters && is_string($specialCharacters))
        {
            $this->_specialCharacters = $specialCharacters;
        }
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;;
        }

        if (false === is_string($value))
        {
            return false;
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_maxWord >= str_word_count($value, 1, $this->_specialCharacters));
        }

        return ($this->_maxWord > strlen($value));
    }

    /**
     * Gets the maximum number of words in a string.
     *
     * @return int representing the maximum number of words allowed for the string.
     */
    public function getMaxValue()
    {
        return $this->_maxWord;
    }
}

/**
 * A validator that ensures that the number of words in a string is not
 * less than a minimum.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 30, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMinWordCountValidator extends SpicaValidator
{
    /**
     * Minimum number of words of the string.
     *
     * @var int
     */
    protected $_minWord;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_rangeBoundaryType;

    /**
     * A list of additional characters that are considered valid characters to compose a word.
     *
     * @var string
     */
    protected $_specialCharacters = 'àáạảãâậẫấầẩăặằắẳặẵçòóọỏõơởớợờôồốộổỗùúụủũừứưửữựỉịíìÀÁẠẢÃÂẬẪẤẦẨĂẶẰẮẲẶẴÇÒÓỌỎÕƠỞỚỢỜÔỒỐỘỔỖÙÚỤỦŨỪỨƯỬỮỰỈỊÍÌüÜñÑäÄöÖüß€$¿₧';

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int $minWord minimum number of words in a string
     * @param int $rangeBoundaryType This parameter determines how the value
     *            will be compared and can have 2 values.
     *            SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *            SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     * @param string $specialCharacters A list of additional characters that are
     *               considered valid characters to compose a word
     */
    public function __construct($minWord, $rangeBoundaryType = 1, $violationMessage, $required = true, $specialCharacters = null)
    {
        $this->_minWord          = $minWord;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $rangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $rangeBoundaryType)
        {
            throw new InvalidArgumentException('The $rangeBoundaryType argument must be 0 or 1.');
        }

        $this->_rangeBoundaryType = $rangeBoundaryType;
        $this->_required          = (bool) $required;

        if (null !== $specialCharacters && is_string($specialCharacters))
        {
            $this->_specialCharacters = $specialCharacters;
        }
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (false === is_string($value))
        {
            return false;
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_rangeBoundaryType)
        {
            return ($this->_minWord <= str_word_count($value, 1, $this->_specialCharacters));
        }

        return ($this->_minWord < strlen($value));
    }

    /**
     * Gets the minimum number of words in a string.
     *
     * @return int representing the minimum number of words allowed for the string.
     */
    public function getMinValue()
    {
        return $this->_minWord;
    }
}

/**
 * A validator that ensures that the number of words in a string is not less than
 * a minimum and is not greater a maximum.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 30, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaWordCountRangeValidator extends SpicaRangeValidator
{
    /**
     * Maximum word count in a string.
     *
     * @var int
     */
    protected $_maxWordCount;

    /**
     * Minimum word count in a string.
     *
     * @var int
     */
    protected $_minWordCount;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_minRangeBoundaryType;

    /**
     * Determines how the value will be compared and can have 2 values.
     *
     *  + SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *  + SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     *
     * @var int
     */
    protected $_maxRangeBoundaryType;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param int|float $minLength minimum length for the string
     * @param int       $minRangeBoundaryType This parameter determines how the value
     *                  will be compared and can have 2 values.
     *                  SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                  SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param int|float $maxLength maximum length for the string
     * @param int       $maxRangeBoundaryType This parameter determines how the value
     *                  will be compared and can have 2 values.
     *                  SpicaRangeBoundaryType::INCLUSIVE - The value is included in the specified range
     *                  SpicaRangeBoundaryType::EXCLUSIVE - The value is not included in the specified range
     * @param string $violationMessage violation message to use if the validation fails
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($min, $minRangeBoundaryType = 1, $max, $maxRangeBoundaryType = 1, $violationMessage, $required = true)
    {
        $this->_minWordCount     = $min;
        $this->_maxWordCount     = $max;
        $this->_violationMessage = $violationMessage;

        if (SpicaRangeBoundaryType::INCLUSIVE !== $minRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $minRangeBoundaryType)
        {
            throw new InvalidArgumentException('The $minRangeBoundaryType argument must be 0 or 1.');
        }

        if (SpicaRangeBoundaryType::INCLUSIVE !== $maxRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE !== $maxRangeBoundaryType)
        {
            throw new InvalidArgumentException('The $maxRangeBoundaryType argument must be 0 or 1.');
        }

        $this->_maxRangeBoundaryType = $maxRangeBoundaryType;
        $this->_minRangeBoundaryType = $minRangeBoundaryType;
        $this->_required             = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (false === is_string($value))
        {
            return false;
        }

        $count = (int) preg_match_all(SpicaValidationPattern::WORD_UTF8, $string, $matches);

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::INCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($count < $this->_minWordCount || $this->_maxWordCount < $count);
        }

        if (SpicaRangeBoundaryType::EXCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::INCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($count <= $this->_minWordCount || $this->_maxWordCount < $count);
        }

        if (SpicaRangeBoundaryType::INCLUSIVE === $this->_minRangeBoundaryType && SpicaRangeBoundaryType::EXCLUSIVE === $this->_maxRangeBoundaryType)
        {
            return ($count < $this->_minWordCount || $this->_maxWordCount <= $count);
        }

        return ($this->_minWordCount >= $count || $this->_maxWordCount <= $count);
    }

    /**
     * Gets the minimum word count in a string.
     *
     * @return int representing the minimum word count in a string.
     */
    public function getMinValue()
    {
        return $this->_minWordCount;
    }

    /**
     * Gets the maximum word count in a string.
     *
     * @return int representing the maximum word count in a string.
     */
    public function getMaxValue()
    {
        return $this->_maxWordCount;
    }
}

/**
 * Tests a field's textual value against a list of strings. You determine
 * the operator to compare the two
 *
 * If the operator is about EQUAL and the text matches one of the strings, it is valid.
 * For example, you want to have the user type an answer of "Bird", "Cat" or "Dog".
 *
 * If the operator is about NOT EQUAL, and the text matches one of the strings,
 * it is invalid.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 02, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaCompareToListValidator extends SpicaValidator
{
    /**
     * A list of allowed values.
     *
     * @var array
     */
    protected $_list;

    /**
     * Creates a validator with the provided violation message.
     *
     * @param array  $list List of allowed values
     * @param string $violationMessage violation message to use if the validation fails
     * @param int    $operator the operator that determine how comparison works. There are 2 values
     *                 SpicaOperator::EQUAL
     *                 SpicaOperator::NOT_EQUAL
     * @param bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($list, $violationMessage, $operator, $required = true)
    {
        if (SpicaOperator::EQUAL !== $operator && SpicaOperator::NOT_EQUAL !== $operator)
        {
            throw new InvalidArgumentException('Argument $operator value is not valid. Acceptable values: SpicaOperator::EQUAL and SpicaOperator::NOT_EQUAL.');
        }

        if (false === is_array($list) || empty($list))
        {
            throw new InvalidArgumentException('The argument $list must be a not empty array.');
        }

        $this->_list             = $list;
        $this->_violationMessage = $violationMessage;
        $this->_required         = (bool) $required;
    }

    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (true === spica_valid_empty_string($value))
        {
            return (false === $this->_required);
        }

        if (false === is_string($value))
        {
            return false;
        }

        switch ($this->_operator)
        {
            case SpicaOperator::EQUAL:
                return in_array($value, $this->_list);

            case SpicaOperator::NOT_EQUAL:
                return !in_array($value, $this->_list);
        }
    }
}

/**
 * Evaluates the selected indexes of a select option list, radio button list, or
 * check box list against a list of ranges. This way, you can have several valid
 * selected indexes.
 *
 * The value in question must be an array or NULL or a false will be returned.
 * If you want to compare a string with a list of values, @see SpicaCompareToStringValidator
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 02, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaSelectedIndexRangesValidator extends SpicaCompareToListValidator
{
    /**
     * (non-PHPdoc)
     * @see library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        // No data is provided but this value is optional (not mandatory)
        if (null === $value || is_array($value) && empty($value))
        {
            return (false === $this->_required);
        }

        if (false === is_array($value))
        {
            return false;
        }

        // Reduce class properties lookup overhead in a loop
        $op   = $this->_operator;
        $list = $this->_list;

        foreach ($value as $val)
        {
            switch ($op)
            {
                case SpicaOperator::EQUAL:

                    if (false === in_array($value, $list))
                    {
                        return false;
                    }

                    break;

                case SpicaOperator::NOT_EQUAL:

                    if (true === in_array($value, $list))
                    {
                        return false;
                    }

                    break;
            }
        }

        return true;
    }
}

/**
 * This validator that tests a field's textual value against another value that
 * you supply. You determine the data type  of the values and the operator to
 * compare the two. E.x: compare user password input with the one in the database.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 02, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaCompareToStringValidator extends SpicaValidator
{
    /**
     * Operator for comparison.
     *
     * @var int
     */
    protected $_operator;

    /**
     * String to compare with the validated string.
     *
     * @var string
     */
    protected $_string;

    /**
     * Creates a validator with the provided violation message.
     *
     * @throws InvalidArgumentException
     * @param  string $string           Standard string to compare with the string in question.
     * @param  string $violationMessage violation message to use if the validation fails
     * @param  int    $operator         the operator that determine how comparison works
     *                                     SpicaOperator::NOT_EQUAL
     *                                     SpicaOperator::EQUAL
     * @param  bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($string, $violationMessage, $operator, $required = true)
    {
        if (SpicaOperator::EQUAL !== $operator && SpicaOperator::NOT_EQUAL !== $operator)
        {
            throw new InvalidArgumentException('Argument $operator value is not valid. Acceptable values: SpicaOperator::EQUAL and SpicaOperator::NOT_EQUAL.');
        }

        if (false === is_string($string) || 0 === strlen($string))
        {
            throw new InvalidArgumentException('The argument $string must be a not empty string.');
        }

        $this->_string           = $string;
        $this->_violationMessage = $violationMessage;
        $this->_required         = $required;
        $this->_operator         = $operator;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        $this->_value = $value;

        if (true === spica_valid_empty_string($value))
        {
            return false === $this->_required;
        }

        if (false === is_string($value))
        {
            return false;
        }

        switch ($this->_operator)
        {
            case SpicaOperator::EQUAL:
                return ($this->_string === $value);

            case SpicaOperator::NOT_EQUAL:
                return ($this->_string !== $value);
        }
    }

    /**
     * Gets the string to compare.
     *
     * @return string
     */
    public function getStringToCompare()
    {
        return $this->_string;
    }
}

/**
 * This validator that tests the values of two fields to each other.
 * You select the operator that is used in the comparison and the data type.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 02, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaCompareTwoFieldsValidator extends SpicaValidator
{
    /**
     * The first field name.
     *
     * @var string
     */
    protected $_firstField;

    /**
     * The first field name.
     *
     * @var string
     */
    protected $_secondField;

    /**
     * The first field value is required or not.
     *
     * @var bool
     */
    protected $_required = true;

    /**
     * Operator for comparison.
     *
     * @var int
     */
    protected $_operator;

    /**
     * Creates a validator with the provided violation message.
     *
     * @throws InvalidArgumentException
     * @param  string $firstFieldName  The first field name.
     * @param  string $secondFieldName The second field name.
     * @param  int    $operator        Comparison operator. Defaults to null, means SpicaOperator::EQUAL
     * @param  string $violationMessage violation message to use if the validation fails
     * @param  bool   $required Determines whether this value is required. Defaults to true
     */
    public function __construct($firstField, $secondField, $violationMessage, $operator = null, $required = true)
    {
        if (null === $operator)
        {
            $operator = SpicaOperator::EQUAL;
        }

        if (false === SpicaOperator::isDefined($operator))
        {
            throw new InvalidArgumentException('Argument $operator value is not valid. ');
        }

        if (null === $firstField)
        {
            throw new InvalidArgumentException('Argument $firstField is required. ');
        }

        if (null === $secondField)
        {
            throw new InvalidArgumentException('Argument $secondField is required. ');
        }

        $this->_firstField       = $firstField;
        $this->_secondField      = $secondField;
        $this->_violationMessage = $violationMessage;
        $this->_required         = $required;
        $this->_operator         = $operator;
    }

    /**
     * (non-PHPdoc)
     * @see trunk/library/spica/core/validator/SpicaValidator#isValid()
     */
    public function isValid($value = null)
    {
        if (!isset($_POST[$this->_firstField]) || !isset($_POST[$this->_secondField]))
        {
            return (false === $this->_required);
        }

        if (spica_valid_empty($_POST[$this->_firstField]) || spica_valid_empty($_POST[$this->_secondField]))
        {
            return (false === $this->_required);
        }

        $first  = $_POST[$this->_firstField];
        $second = $_POST[$this->_secondField];

        switch ($this->_operator)
        {
            case SpicaOperator::EQUAL:
                return ($first === $second);

            case SpicaOperator::GREATER_EQUAL:
                return ($first >= $second);

            case SpicaOperator::GREATER_THAN:
                return ($first > $second);

            case SpicaOperator::LESS_EQUAL:
                return ($first <= $second);

            case SpicaOperator::LESS_THAN:
                return ($first < $second);

            case SpicaOperator::NOT_EQUAL:
                return ($first !== $second);
        }
    }
}

/**
 * Constants used to represent boolean operators.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      May 19, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaOperator
{
    /**
     * Equality comparison.
     *
     * @var int
     */
    const EQUAL         = 1;

    /**
     * Equal or greater than comparison.
     *
     * @var int
     */
    const GREATER_EQUAL = 2;

    /**
     * Greater than comparison.
     *
     * @var int
     */
    const GREATER_THAN  = 3;

    /**
     * Equal or less than comparison.
     *
     * @var int
     */
    const LESS_EQUAL    = 4;

    /**
     * Less than comparison.
     *
     * @var int
     */
    const LESS_THAN     = 5;

    /**
     * Not equality comparison.
     *
     * @var int
     */
    const NOT_EQUAL     = 6;

    /**
     * Checks if the operator in question is defined.
     *
     * @param  int $operator
     * @return bool
     */
    public static function isDefined($operator)
    {
        return in_array($operator, range(1, 6, 1));
    }
}

/**
 * A validation report is composed of validation items each one is characterized
 * by a severity and has a user message.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaValidationReport
{

}

/**
 * This class contains list of indicators to determine how to compare values.
 *
 * @category   spica
 * @package    validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 26, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaRangeBoundaryType
{
    const INCLUSIVE = 1;
    const EXCLUSIVE = 0;
}

/**
 * A SpicaValidatorException is an exception thrown by the validate() method of a
 * Validator to indicate that validation failed.
 *
 * @category   spica
 * @package    core
 * @subpackage validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      March 26, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaValidatorException extends Exception {}

/**
 * <p>Validating functions.</p>
 *
 * @category   spica
 * @package    validator
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.2
 * @since      March 22, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Common.php 1869 2011-01-07 18:55:25Z pcdinh $
 */

/**
 * Returns true if it is a U.S. telephone number in the format (###) ###-####,
 * where the area code is optional, the parentheses around the area code are
 * optional and could be replaced with a dash, and there is optional spacing between
 * the number groups, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_phone_us($value)
{
    include_once 'library/spica/core/validator/Pattern.php';
    return (bool) preg_match(SpicaValidationPattern::PHONE_US, $value);
}

/**
 * Returns true if it is a U.S. telephone number in the format (###) ###-####,
 * as in spica_valid_usphone(), except allow for an optional one- to five-digit
 * extension specified with an "x", "ext", or "ext." and optional spacing,
 * false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_phone_ext_us($value)
{
    include_once 'library/spica/core/validator/Pattern.php';
    return (bool) preg_match(SpicaValidationPattern::PHONE_US_EXT, $value);
}

/**
 * Returns true if it is a U.S. currency starting with a $ and followed by any
 * number with at most two optional decimal digits, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_currency_us($value)
{
    include_once 'library/spica/core/validator/Pattern.php';
    return (bool) preg_match(SpicaValidationPattern::CURRENCY_US, $value);
}

/**
 * Returns true if it is a five-digit U.S. Zip Code with an optional dash
 * and four-digit extension, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_zipcode_us($value)
{
    include_once 'library/spica/core/validator/Pattern.php';
    return (bool) preg_match(SpicaValidationPattern::ZIPCODE_US, $value);
}

/**
 * Returns true if it is a Canadian Postal Code in the format L#L #L# (where L is a letter).
 * There is a restriction placed on the first letter in the Postal Code to ensure
 * that a valid province, territory, or region is specified, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_portalcode_ca($value)
{
    include_once 'library/spica/core/validator/Pattern.php';
    return (bool) preg_match(SpicaValidationPattern::POSTALCODE_CA, $value);
}

/**
 * Returns true if it is a social security number in the format ###-##-####,
 * where the dashes are optional and the three groups can have optional spacing
 * between them, false otherwise.
 *
 * SSN must be 9 digit number
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_ssn($value)
{
    return (bool) preg_match(SpicaValidationPattern::SSN, $value);
}

/**
 * Returns true if it is less than $max, false otherwise.
 *
 * @param  mixed $value
 * @param  mixed $max
 * @return bool
 */
function spica_valid_less_than($value, $max = null)
{
    return ($value < $max);
}

/**
 * Returns true if it is greater than $min, false otherwise.
 *
 * @param  mixed $value
 * @param  mixed $min
 * @return bool
 */
function spica_valid_greater_than($value, $min = null)
{
    return ($value > $min);
}

/**
 * Returns true if it is a valid hexadecimal format, false
 * otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_hex($value)
{
    return ctype_xdigit($value);
}

/**
 * Returns true if it is a valid float value, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_float($value)
{
    return (floatval($value) == $value);
}

/**
 * Returns true if every character is a digit, false otherwise.
 * This is just like spica_valid_integer(), except there is no upper limit.
 *
 * @param  mixed $value
 * @return mixed
 */
function spica_valid_digits($value)
{
    return (is_string($value) || is_int($value) || is_float($value)) && ctype_digit((string)$value);
}

/**
 * Returns true if it is greater than or equal to $min and less
 * than or equal to $max, false otherwise.
 *
 * If $inclusive is set to false, then the value must be strictly greater than $min and
 * strictly less than $max.
 *
 * @param  mixed $value
 * @param  mixed $min Minimum value
 * @param  mixed $max Maximum value
 * @param  bool  $inclusive
 * @return bool
 */
function spica_valid_between($value, $min, $max, $inclusive = true)
{
    if ($value > $min && $value < $max)
    {
        return true;
    }

    return ($value >= $min && $value <= $max && $inclusive);
}

/**
 * Returns true if every character is alphabetic, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_alpha($value)
{
    return ctype_alpha($value);
}

/**
 * Returns true if every character is letters (ASCII) and digits, false otherwise.
 * Float number contains decimal separator . is not considered alphanumberic.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_alnum($value)
{
    return (is_int($value) || (is_string($value) && ctype_alnum($value)));
}

/**
 * Returns true if the string to inspect is empty.
 *
 * @param  mixed $value string to inspect
 * @param  bool  consider white space characters as valid
 * @return bool
 */
function spica_valid_empty_string($value = null, $allowWhitespace = false)
{
    if (null === $value)
    {
        return true;
    }

    // integer, float, boolean, array, object, resource.
    if (false === is_string($value))
    {
        return false;
    }

    // if white spaces can't be considered valid (not empty) characters
    if (false  === $allowWhitespace)
    {
        $value = trim($value);
    }

    return ('' === $value);
}

/**
 * Returns true if the value to inspect is empty: array, object, null value
 * or empty string.
 *
 * Boolean value true and false is considered not empty.
 *
 * @param  mixed $value value to inspect
 * @param  bool  consider white space characters as valid
 * @return bool
 */
function spica_valid_empty($value = null, $allowWhitespace = false)
{
    // true and false is considered not empty
    if (true === is_bool($value))
    {
        return false;
    }

    // No object properties or array elements
    if (false  === is_scalar($value))
    {
        return !count($tmp = (array) $value);
    }

    if (true   === is_string($value) && false === $allowWhitespace)
    {
        $value = trim($value, " \x00..\x1F");
    }

    return (bool) !strlen($value);
}

/**
 * Returns true if every character is alphabetical characters, numbers, underscores and dashes only.
 *
 * Modified from the version written by zombor.
 *
 * @param  string $string  input string
 * @param  bool   UTF-8 compatibility
 * @return bool
 */
function spica_valid_alphanum_dash($string, $utf8 = false)
{
    include_once 'library/spica/core/validator/Pattern.php';

    if (false === is_string($string))
    {
        return false;
    }

    if (true === $utf8)
    {
        return (bool) preg_match(SpicaValidationPattern::ALPHANUM_DASH_UTF8, $string);
    }

    return (bool) preg_match(SpicaValidationPattern::ALPHANUM_DASH_ASCII, $string);
}

/**
 * Returns true if the given string contains only letters, numbers, whitespace,
 * dashes, periods, and underscores.
 *
 * Modified from the version written by zombor.
 *
 * @param  mixed $string text to check
 * @return bool
 */
function spica_valid_plain_text($string)
{
    include_once 'library/spica/core/validator/Pattern.php';

    if (false === is_string($string))
    {
        return false;
    }

    return (bool) preg_match(SpicaValidationPattern::SAFE_TEXT, $string);
}

/**
 * Returns true if it is a valid IP format, false otherwise.
 *
 * @param  mixed $value
 * @return bool
 */
function spica_valid_ip($value)
{
    return (bool) ip2long($value);
}

/**
 * Returns true if the value given is a member of a predefined set of values,
 * false otherwise.
 *
 * If $value is a string, the comparison is case sensitive.
 *
 * @param  mixed $value
 * @param  array $set
 * @return bool
 */
function spica_valid_one_of($value, $set)
{
    return in_array($value, $set);
}

/**
 * Returns true if the value given is a valid email format, false otherwise.
 *
 * @param  mixed $email
 * @return bool
 */
function spica_valid_email_format($email)
{
    include_once 'library/spica/core/validator/Pattern.php';
    return (bool) preg_match(SpicaValidationPattern::EMAIL, $email);
}

/**
 * Returns true if the value given is a valid URI, false otherwise.
 *
 * @param  mixed $value The URI to test.
 * @return bool Returns true if the value given is a valid URI, false otherwise.
 */
function spica_valid_uri($value)
{
    include_once 'library/spica/core/validator/Pattern.php';

    if (true === empty($value))
    {
        return false;
    }

    return (bool) preg_match(SpicaValidationPattern::URI, $value, $matches);
}

/**
 * Returns true if the length of a given string is greater than or equal to $min and less
 * than or equal to $max, false otherwise.
 *
 * @param  string $string The string in question
 * @param  int    $min
 * @param  int    $max
 * @param  bool   $inclusive
 * @return bool
 */
function spica_valid_length($string, $min, $max, $inclusive = true)
{
    $length = strlen($string);

    if ($length > $min && $length < $max)
    {
        return true;
    }

    return ($length >= $min && $length <= $max && $inclusive);
}

/**
 * Returns true if the length of a given string is greater than or equal to $min
 *
 * @param  string $string The string in question
 * @param  int    $min
 * @param  bool   $inclusive
 * @return bool
 */
function spica_valid_minlength($string, $min, $inclusive = true)
{
    $length = strlen($string);

    if ($length < $min)
    {
        return false;
    }

    return ($length === $min && $inclusive);
}

/**
 * Returns true if the length of a given string is less than or equal to $max
 *
 * @param  string $string The string in question
 * @param  mixed  $max
 * @param  bool   $inclusive
 * @return bool
 */
function spica_valid_maxlength($string, $max, $inclusive = true)
{
    $length = strlen($string);

    if ($length > $max)
    {
        return false;
    }

    return ($length === $max && $inclusive);
}

/**
 * Returns true if the number of words of a given string is greater than or equal to $min
 *
 * @see    http://vn.php.net/str_word_count
 * @param  string $string The string in question
 * @param  int    $min    The minimum words allowed
 * @param  bool   $inclusive
 * @return bool
 */
function spica_valid_min_wordcount($string, $min, $inclusive = true)
{
    include_once 'library/spica/core/validator/Pattern.php';
    $count = (int) preg_match_all(SpicaValidationPattern::WORD_UTF8, $string, $matches);

    if ($count < $min)
    {
        return false;
    }

    return ($count === $min && $inclusive);
}

/**
 * Returns true if the number of words of a given string is less than or equal to $max
 *
 * @see    http://vn.php.net/str_word_count
 * @param  string $string The string in question
 * @param  int    $max    The maximum words allowed
 * @param  bool   $inclusive
 * @return bool
 */
function spica_valid_max_wordcount($string, $max, $inclusive = true)
{
    include_once 'library/spica/core/validator/Pattern.php';
    $count = (int) preg_match_all(SpicaValidationPattern::WORD_UTF8, $string, $matches);

    if ($count > $max)
    {
        return false;
    }

    return ($length === $max && $inclusive);
}

/**
 * Returns true if it is a valid integer value, false otherwise.
 *
 * @param  int|string $value
 * @return bool
 */
function spica_valid_integer($value)
{
    return is_int($value) || ctype_digit($value);
}

?>